package view.overlay;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Rectangle2D;

import view.Util;


/**
 * Represents a layer to be drawn for the game. As an abtract type,
 * its subtypes are required to implement the getImage() and
 * getPreferredSize() operations.
 * 
 * @deviation
 * Removed 'mBackgroundColor' attribute as it doesn't make sense at this
 * point in the hierarchy.
 * 
 * @deviation
 * getPreferredSize() returns Dimension instead of Size. This was a
 * typo and should have been Dimension in the UML as it was what was
 * intended by the design.
 * 
 * @deviation
 * Added a width (mWidth) and height (mHeight) attribute to the
 * Overlay type so that it may keep track of its size as well as for
 * the below deviation. Also added a resize() operation to notify
 * this Overlay of a size change.
 * 
 * @deviation
 * Added an image attribute since every Overlay will draw to an image.
 * Also added an abstract paint(Graphics) operation so that the
 * subtypes can define how they are painted.
 * 
 * @deviation
 * Added a protected paint() operation so that the subtypes
 * may instruct the Overlay to paint itself.
 * 
 * @deviation
 * Added a imageRequested() operation that serves as a hook for the subtypes
 * of Overlay to perform some operation on itself each time it is requested.
 * 
 * @deviation
 * Added the notion of a location for an Overlay, including getters and setters.
 * 
 * @deviation
 * Added the implementation of a MouseMotionListenern for finer grained control
 * if an Overlay subtype desires it.
 * 
 * @author matt
 *
 */
public abstract class Overlay implements MouseListener, MouseMotionListener {
	
	// Boolean representing if this Overlay is visible.
	private boolean mIsVisible;
	
	// The size of this Overlay
	private int mWidth, mHeight;
	
	// Location of the Overlay
	private int mX, mY;
	
	// The Overlay's image
	private Image mOverlayImage;
	
	// Whether or not the Overlay should propogate MouseListener events
	private boolean mIsActive;
	
	// Whether or not it has been drawn since it was set visible
	private boolean mHasDrawn;
	
	
	public Overlay() {
		setVisible(true);
	}
	
	
	
	/**
	 * Retuns an Image representation of this Overlay if it is visible,
	 * otherwise it may return <code>null.</code>
	 * 
	 * 
	 * @return		Image	Returns an Image if visible, NULL otherwise.
	 */
	public Image getImage() {
		if (isVisible()) {
			this.imageRequested();
			return (getBufferImage());
		}
		
		return (null);
	}
	

	/**
	 * Forces the Overlay to paint itself regardless
	 * of its visibility.
	 */
	protected void paint() {
		// has drawn at least once
		mHasDrawn = true;
		
		if (mOverlayImage == null) {
			mOverlayImage = Util.createImageBuffer(getWidth(), getHeight());
		}
		
		if (mOverlayImage == null) return;
		
		Graphics2D g = (Graphics2D)mOverlayImage.getGraphics();
		g.setColor(new Color(0, 0, 0, 0));
		
		Composite oldC = g.getComposite();
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
		g.fillRect(0, 0, getWidth(), getHeight());
		g.setComposite(oldC);
//		
//		g.setColor(new Color(255, 255, 255, 0));
//		g.clearRect(0, 0, this.getWidth(), this.getHeight());
//		
		if (g != null) {
			paint(g);
		}
	}
	
	
	
	/**
	 * Returns true if this Overlay is visible,
	 * false otherwise.
	 * 
	 * @return	boolean		True iff the Overlay is visible.
	 */
	public boolean isVisible() {
		return (mIsVisible);
	}
	
	/**
	 * Resize the Overlay.
	 * 
	 * @param width
	 * @param height
	 */
	public void resize(int width, int height) {
//		System.out.format("Overlay resized from: w=%d h=%d to w=%d h=%d\n", this.mWidth, this.mHeight, width, height);
		this.mWidth = width;
		this.mHeight = height;
		
		// We need to update the image
		mOverlayImage = Util.createImageBuffer(width, height);
	}
	
	/**
	 * Returns this Overlay's width
	 * 
	 * @return	int		This Overlay's width.
	 */
	public int getWidth() { return (mWidth); }
	
	/**
	 * Sets the width of the Overlay
	 * @param width		int		The Width of the Overlay
	 */
	public void setWidth(int width) { resize(width, mHeight); }
	
	/**
	 * Returns the height of this Overlay.
	 * 
	 * @return	int		This Overlay's height.
	 */
	public int getHeight() { return (mHeight); }
	
	/**
	 * Sets the height of this Overlay
	 */
	public void setHeight(int height) { resize(mWidth, height); }
	
	
	
	public void onMousePressed(MouseEvent e) {}
	public void onMouseReleased(MouseEvent e) {}
	public void onMouseEntered(MouseEvent e) {}
	public void onMouseExited(MouseEvent e) {}
	public void onMouseClicked(MouseEvent e) {}
	
	public void mousePressed(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMousePressed(e);
		}
	}
	
	public void mouseReleased(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMouseReleased(e);
		}
	}
	
	public void mouseEntered(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMouseEntered(e);
		}
	}
	
	public void mouseExited(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMouseExited(e);
		}
	}
	
	public void mouseClicked(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMouseClicked(e);
		}
	}
	
	public void mouseDragged(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMouseDragged(e);
		}
	}
	
	public void mouseMoved(MouseEvent e) {
		if (mIsActive && mHasDrawn) {
			onMouseMoved(e);
		}
	}
	
	
	public void onMouseDragged(MouseEvent e) {}
	public void onMouseMoved(MouseEvent e) {}
	
	
	

	/**
	 * Returns the Image object used to draw the Overlay.
	 * 
	 * @return	Image	Image used as a buffer.
	 */
	protected Image getBufferImage() {
		return (mOverlayImage);
	}
	
	
	
	/**
	 * Protected method to be implemented inside each Overlay
	 * subtype that will draw the visual representation to a
	 * Graphics object.
	 * 
	 * @param g	Graphics	The Graphics object.
	 */
	protected abstract void paint(Graphics g);
	
	
	/**
	 * Protected operation to be called when the getImage() operation
	 * is called on this Overlay.
	 */
	protected abstract void imageRequested();
	
	
	/**
	 * Returns the preferred size of this Overlay.
	 * @return	Dimension		The preferred size of this overlay.
	 */
	public abstract Dimension getPreferredSize();




	/**
	 * Sets the visibility of this Overlay. If it is invisible, a 
	 * call to <code>getImage()</code> will return null.
	 * @param visibility
	 */
	public void setVisible(boolean visibility) {
		if ((! this.mIsVisible) && visibility) {
			// changing from invisible to visible
			mHasDrawn = false;
		}
		this.mIsVisible = visibility;
		
		setActive(visibility);
	}


	/**
	 * Gets the X-Coordinate of where to draw this Overlay on the
	 * screen (or image).
	 * 
	 * @return	int		The X-Coordinate
	 */
	public int getX() {
		return mX;
	}

	/**
	 * Sets the X-Coordinate of wehre to draw this Overlay on the
	 * screen (or image).
	 * 
	 * @param mX	int		The new X-Coordinate
	 */
	public void setX(int mX) {
		this.mX = mX;
	}

	/**
	 * Gets the Y-Coordinate of where to draw this Overlay on the
	 * screen (or image).
	 * 
	 * @return	int		The Y-Coordinate
	 */
	public int getY() {
		return mY;
	}

	/**
	 * Sets the Y-Coordinate
	 * 
	 * @param mY	int		The new Y-Coordinate
	 */
	public void setY(int mY) {
		this.mY = mY;
	}

	
	public void setActive(boolean isActive) {
		// If not active, no mouse events will propagate
		mIsActive = isActive;
		
		
		if (! mIsActive) {
			//System.out.format("Overlay::setActive() > %s is no longer active!\n", this.getClass().getSimpleName());
		}
		else {
			//System.out.format("Overlay::setActive() > %s is now active!\n", this.getClass().getSimpleName());
		}
	}
	
	public boolean isActive() {
		return (mIsActive);
	}




}
